package parallelization;

import java.util.*;

public class WorkQueue
{
    private final int nThreads;
    private final PoolWorker[] threads;
    private final Queue<Runnable> queue;
    private Integer running;
    public Boolean isIdle;
    
    public synchronized boolean idle() {
    	boolean isEmpty = false;
    	synchronized (queue) {
    		isEmpty = queue.isEmpty();
		}
    	boolean allWaiting = false;
    	synchronized (running) {
			allWaiting = running == 0;
		}
    	return allWaiting && isEmpty;
    }

    public WorkQueue(int nThreads)
    {
        this.nThreads = nThreads;
        queue = new LinkedList<Runnable>();
        threads = new PoolWorker[nThreads];

        for (int i=0; i<this.nThreads; i++) {
            threads[i] = new PoolWorker();
            threads[i].start();
        }
        running  = 0;
        isIdle = true;
    }
    
    public void kill() {
    	for (int i=0;i<threads.length;i++)
    		threads[i].shouldDie = true;
    	synchronized (queue) {
    		queue.notifyAll();
		}
    }

    public void execute(Runnable r) {
        synchronized(queue) {
            queue.offer(r);
            queue.notify();
        }
    }

    private class PoolWorker extends Thread {
    	
    	public boolean shouldDie = false;
    	
        public void run() {
            Runnable r;
            while (!shouldDie) {
                synchronized(queue) {
                    while (queue.isEmpty() && !shouldDie) {
                        try {
                            queue.wait();
                        }
                        catch (InterruptedException ignored) { }
                    }
                    r =queue.poll();
                }

                // If we don't catch RuntimeException, 
                // the pool could leak threads
                try {
                	synchronized (running) {
						running++;
					}
                    r.run();
                    synchronized (running) {
						running--;
					}
                    synchronized (isIdle) {
						isIdle.notifyAll();
					}
                }
                catch (RuntimeException e) {
                    // You might want to log something here
                }
            }
        }
    }
}
